<?php

use think\facade\Db;
use think\facade\Env;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\JwtFacade;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\Clock\FrozenClock;
use Lcobucci\JWT\Token\RegisteredClaims;
use Lcobucci\JWT\Validation\Constraint;
use think\exception\ValidateException;
use crmeb\services\FormBuilder as Form;

// 应用公共文件

/**
 * @notes 生成密码加密密钥
 * @param string $plaintext
 * @param string $salt
 * @return string
 * @author 段誉
 * @date 2021/12/28 18:24
 */
function create_password(string $plaintext, string $salt): string
{
    return md5($salt . md5($plaintext . $salt));
}


/**
 * @notes 随机生成token值
 * @param string $extra
 * @return string
 * @author 段誉
 * @date 2021/12/28 18:24
 */
function create_token(string $extra = ''): string
{
    return md5($extra . time());
}


/**
 * @notes 截取某字符字符串
 * @param $str
 * @param string $symbol
 * @return string
 * @author 段誉
 * @date 2021/12/28 18:24
 */
function substr_symbol_behind($str, $symbol = '.'): string
{
    $result = strripos($str, $symbol);
    if ($result === false) {
        return $str;
    }
    return substr($str, $result + 1);
}


/**
 * @notes 对比php版本
 * @param string $version
 * @return bool
 * @author 段誉
 * @date 2021/12/28 18:27
 */
function comparePHP(string $version): bool
{
    return version_compare(PHP_VERSION, $version) >= 0 ? true : false;
}


/**
 * @notes 检查文件是否可写
 * @param string $dir
 * @return bool
 * @author 段誉
 * @date 2021/12/28 18:27
 */
function checkDirWrite(string $dir = ''): bool
{
    $route = root_path() . '/' . $dir;
    return is_writable($route);
}


/**
 * 多级线性结构排序
 * 转换前：
 * [{"id":1,"pid":0,"name":"a"},{"id":2,"pid":0,"name":"b"},{"id":3,"pid":1,"name":"c"},
 * {"id":4,"pid":2,"name":"d"},{"id":5,"pid":4,"name":"e"},{"id":6,"pid":5,"name":"f"},
 * {"id":7,"pid":3,"name":"g"}]
 * 转换后：
 * [{"id":1,"pid":0,"name":"a","level":1},{"id":3,"pid":1,"name":"c","level":2},{"id":7,"pid":3,"name":"g","level":3},
 * {"id":2,"pid":0,"name":"b","level":1},{"id":4,"pid":2,"name":"d","level":2},{"id":5,"pid":4,"name":"e","level":3},
 * {"id":6,"pid":5,"name":"f","level":4}]
 * @param array $data 线性结构数组
 * @param string $symbol 名称前面加符号
 * @param string $name 名称
 * @param string $id_name 数组id名
 * @param string $parent_id_name 数组祖先id名
 * @param int $level 此值请勿给参数
 * @param int $parent_id 此值请勿给参数
 * @return array
 */
function linear_to_tree($data, $sub_key_name = 'sub', $id_name = 'id', $parent_id_name = 'pid', $parent_id = 0)
{
    $tree = [];
    foreach ($data as $row) {
        if ($row[$parent_id_name] == $parent_id) {
            $temp = $row;
            $child = linear_to_tree($data, $sub_key_name, $id_name, $parent_id_name, $row[$id_name]);
            if ($child) {
                $temp[$sub_key_name] = $child;
            }
            $tree[] = $temp;
        }
    }
    return $tree;
}


/**
 * @notes 删除目标目录
 * @param $path
 * @param $delDir
 * @return bool|void
 * @author 段誉
 * @date 2022/4/8 16:30
 */
function del_target_dir($path, $delDir)
{
    //没找到，不处理
    if (!file_exists($path)) {
        return false;
    }

    //打开目录句柄
    $handle = opendir($path);
    if ($handle) {
        while (false !== ($item = readdir($handle))) {
            if ($item != "." && $item != "..") {
                if (is_dir("$path/$item")) {
                    del_target_dir("$path/$item", $delDir);
                } else {
                    unlink("$path/$item");
                }
            }
        }
        closedir($handle);
        if ($delDir) {
            return rmdir($path);
        }
    } else {
        if (file_exists($path)) {
            return unlink($path);
        }
        return false;
    }
}


/**
 * @notes 获取数据表字段类型
 * @param string $type
 * @return string
 * @author 段誉
 * @date 2022/6/15 10:11
 */
function getDbFieldType(string $type): string
{
    if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
        $result = 'string';
    } elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) {
        $result = 'float';
    } elseif (preg_match('/(int|serial|bit)/is', $type)) {
        $result = 'int';
    } elseif (preg_match('/bool/is', $type)) {
        $result = 'bool';
    } elseif (0 === strpos($type, 'timestamp')) {
        $result = 'timestamp';
    } elseif (0 === strpos($type, 'datetime')) {
        $result = 'datetime';
    } elseif (0 === strpos($type, 'date')) {
        $result = 'date';
    } else {
        $result = 'string';
    }
    return $result;
}

if (!function_exists('getToken')) {
    /**
     * 生成token
     * @param $user_id
     * @return string
     */
    function getToken($user_id)
    {
        // WGlSGeLHP3nwkWr9kWpIuZCqlkoYxwtELlPwfDjo5g4=
        // hiG8DlOKvtih6AxlZn5XKImZ06yu8I3mkOzaJrEuW8yAv8Jnkw330uMt8AEqQ5LB
        $key = InMemory::base64Encoded(
            env('JWT.JWT_API_KEY')
        );

        $token = (new JwtFacade())->issue(
            new Sha256(),
            $key,
            static fn(
                Builder $builder,
                DateTimeImmutable $issuedAt
            ): Builder => $builder
                ->issuedBy(env('JWT.JWT_API_HOST'))
//                ->issuedBy('https://api.my-awesome-app.io')
                ->permittedFor(env('JWT.JWT_API_HOST'))
//                ->permittedFor('https://client-app.io')
                ->withClaim('user_id', $user_id)
                ->expiresAt($issuedAt->modify('+1 minutes'))
        );

//        var_dump($token->claims()->all());
//        echo $token->toString();

        return $token->toString();
    }
}

if (!function_exists('getUserId')) {
    function getUserId($jwt)
    {
//        $jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NTg2OTYwNTIsIm5iZiI6MT'
//            . 'Y1ODY5NjA1MiwiZXhwIjoxNjU4Njk2NjUyLCJpc3MiOiJodHRwczovL2FwaS5teS1hd2Vzb'
//            . '21lLWFwcC5pbyIsImF1ZCI6Imh0dHBzOi8vY2xpZW50LWFwcC5pbyJ9.yzxpjyq8lXqMgaN'
//            . 'rMEOLUr7R0brvhwXx0gp56uWEIfc';

        $key = InMemory::base64Encoded(
            env('JWT.JWT_API_KEY')
        );

//        $token = (new \Lcobucci\JWT\Token\Parser())->parse($jwt);

        try {
            $token = (new JwtFacade())->parse(
                $jwt,
                new Constraint\SignedWith(new Sha256(), $key),
                new Constraint\StrictValidAt(
                    new FrozenClock(new DateTimeImmutable())
                )
            );
        } catch (\Exception $e) {
            return ['code' => 440, 'msg' => 'invalid token'];
        }

//        var_dump($token->claims()->all());
//        var_dump($token->claims()->get());
        $all = $token->claims()->all();

        var_dump($all);

        return ['code' => 200, 'user_id' => $all['user_id']];
    }
}

if (!function_exists('check_card')) {
    /**
     * 身份证验证
     * @param $card
     * @return bool
     */
    function check_card($card)
    {
        $city = [
            11 => "北京",
            12 => "天津",
            13 => "河北",
            14 => "山西",
            15 => "内蒙古",
            21 => "辽宁",
            22 => "吉林",
            23 => "黑龙江 ",
            31 => "上海",
            32 => "江苏",
            33 => "浙江",
            34 => "安徽",
            35 => "福建",
            36 => "江西",
            37 => "山东",
            41 => "河南",
            42 => "湖北 ",
            43 => "湖南",
            44 => "广东",
            45 => "广西",
            46 => "海南",
            50 => "重庆",
            51 => "四川",
            52 => "贵州",
            53 => "云南",
            54 => "西藏 ",
            61 => "陕西",
            62 => "甘肃",
            63 => "青海",
            64 => "宁夏",
            65 => "新疆",
            71 => "台湾",
            81 => "香港",
            82 => "澳门",
            91 => "国外 "
        ];
        $tip = "";
        $match = "/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/";
        $pass = true;
        if (!$card || !preg_match($match, $card)) {
            //身份证格式错误
            $pass = false;
        } else {
            if (!$city[substr($card, 0, 2)]) {
                //地址错误
                $pass = false;
            } else {
                //18位身份证需要验证最后一位校验位
                if (strlen($card) == 18) {
                    $card = str_split($card);
                    //∑(ai×Wi)(mod 11)
                    //加权因子
                    $factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
                    //校验位
                    $parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2];
                    $sum = 0;
                    $ai = 0;
                    $wi = 0;
                    for ($i = 0; $i < 17; $i++) {
                        $ai = $card[$i];
                        $wi = $factor[$i];
                        $sum += $ai * $wi;
                    }
                    $last = $parity[$sum % 11];
                    if ($parity[$sum % 11] != $card[17]) {
                        //                        $tip = "校验位错误";
                        $pass = false;
                    }
                } else {
                    $pass = false;
                }
            }
        }
        if (!$pass) {
            return false;
        }/* 身份证格式错误*/
        return true;/* 身份证格式正确*/
    }
}
if (!function_exists('check_link')) {
    /**
     * 地址验证
     * @param string $link
     * @return false|int
     */
    function check_link(string $link)
    {
        return preg_match("/^(http|https|ftp):\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\’:+!]*([^<>\”])*$/", $link);
    }
}
if (!function_exists('check_phone')) {
    /**
     * 手机号验证
     * @param $phone
     * @return false|int
     */
    function check_phone($phone)
    {
        return preg_match("/^1[3456789]\d{9}$/", $phone);
    }
}

if (!function_exists('create_form')) {
    /**
     * 表单生成方法
     * @param string $title
     * @param array $field
     * @param $url
     * @param string $method
     * @return array
     * @throws \FormBuilder\Exception\FormBuilderException
     */
    function create_form(string $title, array $field, $url, string $method = 'POST')
    {
        $form = Form::createForm((string)$url);//提交地址
        $form->setMethod($method);//提交方式
        $form->setRule($field);//表单字段
        $form->setTitle($title);//表单标题
        $rules = $form->formRule();
        $title = $form->getTitle();
        $action = $form->getAction();
        $method = $form->getMethod();
        $info = '';
        $status = true;
        $methodData = ['POST', 'PUT', 'GET', 'DELETE'];
        if (!in_array(strtoupper($method), $methodData)) {
            throw new ValidateException('请求方式有误');
        }
        return compact('rules', 'title', 'action', 'method', 'info', 'status');
    }
}

/**
 * CMF密码比较方法,所有涉及密码比较的地方都用这个方法
 * @param string $password 要比较的密码
 * @param string $passwordInDb 数据库保存的已经加密过的密码
 * @return boolean 密码相同，返回true
 */
function cmf_compare_password($password, $passwordInDb)
{
    if (strpos($passwordInDb, "###") === 0) {
        return cmf_password($password) == $passwordInDb;
    } else {
        return cmf_password_old($password) == $passwordInDb;
    }
}

/**
 * CMF密码加密方法 (X2.0.0以前的方法)
 * @param string $pw 要加密的原始密码
 * @return string
 */
function cmf_password_old($pw)
{
    $decor = md5(config('database.connections.mysql.prefix'));
    $mi = md5($pw);
    return substr($decor, 0, 12) . $mi . substr($decor, -4, 4);
}

/**
 * CMF密码加密方法
 * @param string $pw 要加密的原始密码
 * @param string $authCode 加密字符串
 * @return string
 */
function cmf_password($pw, $authCode = '')
{
    if (empty($authCode)) {
        $authCode = config('database.authcode');
    }
    $result = "###" . md5(md5($authCode . $pw));
    return $result;
}

if (!function_exists('makeCode')) {
    function makeCode()
    {
        $count = Db::name('user')->where('user_type', 2)->count();
        $count = $count + 1;
        $start = 11111111;
        $end = 99999999;
        $leng = strlen($count);
        if ($leng > 1) {
            $num = $leng - 1;
            $start = substr($start, 0, strlen($start) - $num);
            $end = substr($end, 0, strlen($end) - $num);
        }
        return rand($start, $end) . $count;
    }
}

if (!function_exists('makeWorkerCode')) {
    function makeWorkerCode()
    {
        $count = Db::name('repair_worker')->count();
        $count = $count + 1;
        $start = 11111111;
        $end = 99999999;
        $leng = strlen($count);
        if ($leng > 1) {
            $num = $leng - 1;
            $start = substr($start, 0, strlen($start) - $num);
            $end = substr($end, 0, strlen($end) - $num);
        }
        return rand($start, $end) . $count;
    }
}

if (!function_exists('cmf_check_mobile')) {
    /**
     * 检查手机格式，中国手机不带国家代码，国际手机号格式为：国家代码-手机号
     * @param $mobile
     * @return bool
     */
    function cmf_check_mobile($mobile)
    {
        if (preg_match('/(^(13\d|14\d|15\d|16\d|17\d|18\d|19\d)\d{8})$/', $mobile)) {
            return true;
        } else {
            if (preg_match('/^\d{1,4}-\d{5,11}$/', $mobile)) {
                if (preg_match('/^\d{1,4}-0+/', $mobile)) {
                    //不能以0开头
                    return false;
                }

                return true;
            }

            return false;
        }
    }
}

if (!function_exists('cmf_get_verification_code')) {
    /**
     * 检查手机或邮箱是否还可以发送验证码,并返回生成的验证码
     * @param string $account 手机或邮箱
     * @param integer $length 验证码位数,支持4,6,8
     * @return string 数字验证码
     */
    function cmf_get_verification_code($account, $length = 6)
    {
        if (empty($account)) {
            return false;
        }
        $verificationCodeQuery = Db::name('verification_code');
        $currentTime = time();
        $maxCount = 5;
        $findVerificationCode = $verificationCodeQuery->where('account', $account)->find();
        $result = false;
        if (empty($findVerificationCode)) {
            $result = true;
        } else {
            $sendTime = $findVerificationCode['send_time'];
            $todayStartTime = strtotime(date('Y-m-d', $currentTime));
            if ($sendTime < $todayStartTime) {
                $result = true;
            } else {
                if ($findVerificationCode['count'] < $maxCount) {
                    $result = true;
                }
            }
        }

        if ($result) {
            switch ($length) {
                case 4:
                    $result = rand(1000, 9999);
                    break;
                case 6:
                    $result = rand(100000, 999999);
                    break;
                case 8:
                    $result = rand(10000000, 99999999);
                    break;
                default:
                    $result = rand(100000, 999999);
            }
        }

        return $result;
    }
}


if (!function_exists('object_array')) {
    //这是一个对象，要取其中的内容也可以 取得，利用object->属性  可以取得，但是习惯数组处理数据，下面是一个object(stdclass)转成 数组的函数
    function object_array($array)
    {
        if (is_object($array)) {
            $array = (array)$array;
        }
        if (is_array($array)) {
            foreach ($array as $key => $value) {
                $array[$key] = object_array($value);
            }
        }
        return $array;
    }
}


if (!function_exists('send_verification_code')) {
    /**
     * 发送验证码
     * @param int $mobile 手机号码
     * @param int|string $code 验证码
     * @return mixed
     */
    function send_verification_code($mobile, $code)
    {
        $res = SendMsg::send($mobile, 'SMS_251605437', ['code' => $code]);
        return $res;
    }
}

if (!function_exists('formatHex')) {
    // 工具函数
    function formatHex($dec)
    {
        $hex = gmp_strval(gmp_init($dec, 10), 16);
        $len = strlen($hex);
        if ($len == 64) {
            return $hex;
        }
        if ($len < 64) {
            $hex = str_pad($hex, 64, "0", STR_PAD_LEFT);
        } else {
            $hex = substr($hex, $len - 64, 64);
        }
        return $hex;
    }
}


if (!function_exists('http_post')) {
    /**
     * @param $argument
     * @param $url
     * @return mixed
     */
    function http_post($argument, $url)
    {
        $data_string = json_encode($argument);
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                'Content-Type: application/json',
                'Content-Length: ' . strlen($data_string)
            )
        );
        $result = curl_exec($ch);
        $result = json_decode($result, true);
        return $result;
    }
}


